﻿using System;
using System.Collections.Generic;
using System.Drawing;

namespace Charting
{
    public sealed class Series
    {
        // 图表
        private Plot mChart;
        // 点列表
        private DataPointCollection mPoints;
        // 绘制点列表
        private List<PointF> mDrawPoints;
        // 画笔
        private Pen mLinePen;
        //
        private bool mVisible;
        // 批量更新标记
        private bool mBatchUpdate;
        // 批量更新计数
        private int mBatchUpdateCount;
        //
        private bool mIsValueShownAsLabel;
        private Color mPointColor;
        private Color mLabelForeColor;
        private Color mLabelBackColor;
        private int mPointSize;
        

        public Series()
        {
            mChart = null;
            mPoints = new DataPointCollection(this);
            mDrawPoints = new List<PointF>();
            mLinePen = new Pen(Color.Black);
            mVisible = true;
            mBatchUpdate = false;
            mBatchUpdateCount = 0;
            mIsValueShownAsLabel = false;
            mPointColor = Color.Black;
            mLabelForeColor = Color.White;
            mLabelBackColor = Color.Black;
            mPointSize = 0;
        }

        public Series(Plot chart)
        {
            mChart = chart;
            mPoints = new DataPointCollection(this);
            mDrawPoints = new List<PointF>();
            mLinePen = new Pen(Color.Black);
            mVisible = true;
            mBatchUpdate = false;
            mBatchUpdateCount = 0;
            mIsValueShownAsLabel = false;
            mPointColor = Color.Black;
            mLabelForeColor = Color.White;
            mLabelBackColor = Color.Black;
            mPointSize = 0;
        }

        public void Dispose()
        {
            mDrawPoints.Clear();
            if (mLinePen != null)
                mLinePen.Dispose();
            mChart = null;
        }

        public void BeginUpdate()
        {
            mBatchUpdate = true;
            ++mBatchUpdateCount;
        }

        public void EndUpdate()
        {
            --mBatchUpdateCount;
            if (mBatchUpdateCount > 0)
                return;
            mBatchUpdate = false;
            UpdatePoints();
            ChartUpdateContent();
        }
        /// <summary>
        /// 标题，通常用于绘制Legend
        /// </summary>
        public string Title { get; set; }
        /// <summary>
        /// 绘图数据
        /// </summary>
        public DataPointCollection Points
        {
            get { return mPoints; }
        }

        /// <summary>
        /// 设置和获取曲线的颜色
        /// </summary>
        public Color LineColor
        {
            set
            {
                if (mLinePen.Color != value)
                {
                    mLinePen.Color = value;
                    BeginUpdate();
                    foreach (DataPoint dp in mPoints)
                        dp.LineColor = value;
                    EndUpdate();
                }
            }

            get
            {
                return mLinePen.Color;
            }
        }

        /// <summary>
        /// 设置和获取网格线的大小
        /// </summary>
        public float LineWidth
        {
            set
            {
                if (mLinePen.Width != value)
                {
                    mLinePen.Width = value;
                    ChartUpdateContent();
                }
            }

            get
            {
                return mLinePen.Width;
            }
        }

        public bool Visible
        {
            set
            {
                if (mVisible != value)
                {
                    mVisible = value;
                    ChartUpdateContent();
                }
            }

            get
            {
                return mVisible;
            }
        }
        /// <summary>
        /// 是否显示数据标签
        /// </summary>
        public bool IsValueShownAsLabel
        {
            get { return mIsValueShownAsLabel; }
            set
            {
                if (mIsValueShownAsLabel != value)
                {
                    mIsValueShownAsLabel = value;
                    BeginUpdate();
                    foreach (DataPoint dp in mPoints)
                        dp.IsValueShownAsLabel = value;
                    EndUpdate();
                }
            }
        }

        public Color PointColor
        {
            get { return mPointColor; }
            set
            {
                if (mPointColor != value)
                {
                    mPointColor = value;
                    BeginUpdate();
                    foreach (DataPoint dp in mPoints)
                        dp.PointColor = value;
                    EndUpdate();
                }
            }
        }
        /// <summary>
        /// 数据标签前景色
        /// </summary>
        public Color LabelForeColor
        {
            get { return mLabelForeColor; }
            set
            {
                if (mLabelForeColor != value)
                {
                    mLabelForeColor = value;
                    BeginUpdate();
                    foreach (DataPoint dp in mPoints)
                        dp.LabelForeColor = value;
                    EndUpdate();
                }
            }
        }
        /// <summary>
        /// 数据标签背景色
        /// </summary>
        public Color LabelBackColor
        {
            get { return mLabelBackColor; }
            set
            {
                if (mLabelBackColor != value)
                {
                    mLabelBackColor = value;
                    BeginUpdate();
                    foreach (DataPoint dp in mPoints)
                        dp.LabelBackColor = value;
                    EndUpdate();
                }
            }
        }
        /// <summary>
        /// 数据点大小
        /// </summary>
        public int PointSize
        {
            get { return mPointSize; }
            set
            {
                if (mPointSize != value)
                {
                    mPointSize = value;
                    BeginUpdate();
                    foreach (DataPoint dp in mPoints)
                        dp.PointSize = value;
                    EndUpdate();
                }
            }
        }

        internal void UpdatePoints()
        {
            mDrawPoints.Clear();

            if (mChart == null)
                return;

            if (mPoints.Count == 0)
                return;

            if (mBatchUpdate)
                return;

            float minYValue = mChart.AxisY.Minimum - 1.0F;
            if (mChart.PlotType == EPlotType.Line)
            {
                foreach (DataPoint pt in mPoints)
                    mDrawPoints.Add(mChart.CoordToPosition(pt.X, pt.Y));
            }
            else
            {
                foreach (DataPoint pt in mPoints)
                    mDrawPoints.Add(mChart.CoordToPosition(pt.X, pt.Y));
                if (mPoints.Count > 1)
                {
                    if (Math.Abs(mPoints[0].X) != Math.Abs(mPoints[mPoints.Count - 1].X)
                        || Math.Abs(mPoints[0].Y) != Math.Abs(mPoints[mPoints.Count - 1].Y))
                    {
                        mDrawPoints.Add(mChart.CoordToPosition(mPoints[0].X, mPoints[0].Y));
                    }
                }
            }
        }

        internal void Draw(Graphics g, float x, float y)
        {
            if (!mVisible)
                return;

            if (mDrawPoints.Count == 0)
                return;

            if (mChart == null)
                return;

            RectangleF bound = new RectangleF();
            bound.X = mChart.CoordBounds.X + x;
            bound.Y = mChart.CoordBounds.Y + y;
            bound.Width = mChart.CoordBounds.Width;
            bound.Height = mChart.CoordBounds.Height;

            Region oldClip = g.Clip;
            g.SetClip(bound);
            Color lineColor = mLinePen.Color;
            if (mChart.PlotType == EPlotType.Line)
            {
                for (int i = 0; i < mDrawPoints.Count - 1; ++i)
                {
                    mLinePen.Color = mPoints[i + 1].LineColor;
                    g.DrawLine(mLinePen,
                        mDrawPoints[i].X + x,
                        mDrawPoints[i].Y + y,
                        mDrawPoints[i + 1].X + x,
                        mDrawPoints[i + 1].Y + y);
                    g.DrawLine(mLinePen,
                        mDrawPoints[i].X + x,
                        mDrawPoints[i].Y + y,
                        mDrawPoints[i + 1].X + x,
                        mDrawPoints[i + 1].Y + y);
                }
            }
            else
            {
                for (int i = 0; i < mDrawPoints.Count - 1; ++i)
                {
                    g.DrawLine(mLinePen,
                        mDrawPoints[i].X + x,
                        mDrawPoints[i].Y + y,
                        mDrawPoints[i + 1].X + x,
                        mDrawPoints[i + 1].Y + y);
                    g.DrawLine(mLinePen,
                        mDrawPoints[i].X + x,
                        mDrawPoints[i].Y + y,
                        mDrawPoints[i + 1].X + x,
                        mDrawPoints[i + 1].Y + y);
                }
            }
            mLinePen.Color = lineColor;
            g.Clip = oldClip;

            if (mChart.PlotType == EPlotType.Line)
            {
                int padding = 3;
                StringFormat stringFormat = new StringFormat();
                stringFormat.Alignment = StringAlignment.Near;
                stringFormat.LineAlignment = StringAlignment.Center;
                using (Font font = new Font(mChart.Font, FontStyle.Bold))
                {
                    for (int i = 0; i < mDrawPoints.Count; ++i)
                    {
                        if (mPoints[i].PointSize > 0)
                        {
                            using (Pen pen = new Pen(mPoints[i].PointColor))
                            {
                                g.FillEllipse(pen.Brush, new RectangleF(mDrawPoints[i].X - mPoints[i].PointSize / 2,
                                    mDrawPoints[i].Y - mPoints[i].PointSize / 2, mPoints[i].PointSize, mPoints[i].PointSize));
                            }
                        }
                        if (!mPoints[i].IsValueShownAsLabel)
                            continue;
                        string text = mPoints[i].Y.ToString();
                        SizeF size = g.MeasureString(text, font);
                        RectangleF rect = new RectangleF(mDrawPoints[i].X + x - size.Width / 2 - padding,
                            mDrawPoints[i].Y + y - size.Height - mPoints[i].PointSize - 5,
                            size.Width + 2 * padding, size.Height + 2 * padding);
                        using (Pen pen = new Pen(mPoints[i].LabelBackColor))
                            g.FillRectangle(pen.Brush, rect);
                        using (Pen pen = new Pen(mPoints[i].LabelForeColor))
                            g.DrawString(mPoints[i].Y.ToString(), font, pen.Brush, new RectangleF(rect.X + padding, rect.Y + padding, size.Width, size.Height), stringFormat);
                    }
                }
            }
            else
            {
            }
        }

        internal void ChartUpdateContent()
        {
            if (mChart == null)
                return;
            if (mBatchUpdate)
                return;
            mChart.UpdateContent();
        }
    }
}
